/*
PearReview - The peer review assistant.
Copyright (C) 2009  Dimitrij Pankratz, Anton Musichin
http://www.pearreview.com, info@pearreview.com

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

package com.pearreview.ui;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicButtonUI;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
	
/**
 * UI for a button used in the toolbar, normaly with an icon.
 * 
 * @version 1.0 
 * @author Dimitrij Pankratz, Anton Musichin
 */
public class ToolBarButtonUI extends BasicButtonUI {

	/**
	 * The color of the button border.
	 */
	private Color borderColor = new Color(0x66, 0x66, 0x66);
	
	/**
	 * The stroke of the button border.
	 */
	private Stroke borderStroke = new BasicStroke(1.0f);

	/**
	 * Beginning color of the background gradient, while mouse over.
	 */
	protected Color hoverGradientTop = new Color(0xe7, 0xe7, 0xe7);
	
	/**
	 * Ending color of the background gradient, while mouse over.
	 */
	//protected Color hoverGradientBottom = new Color(0xca, 0xca, 0xca);
	protected Color hoverGradientBottom = new Color(0xaaaaaa);
	
	/**
	 * Beginning color of the background gradient, while mouse pressed.
	 */
	protected Color pressedGradientTop = new Color(0xb7, 0xb7, 0xb7);
	
	/**
	 * Ending color of the background gradient, while mouse over.
	 */
	protected Color pressedGradientBottom = new Color(0x90, 0x90, 0x90);
	
	/**
	 * Icon of the button, while mouse is over.
	 */
	protected Icon hoverIcon;
	
	
	//margins
	
	/**
	 * Top margin of the menu.
	 */
	protected int topMargin = 1;
	
	/**
	 * Bottom margin of the menu.
	 */
	protected int bottomMargin = 1;
	
	/**
	 * Left margin of the menu.
	 */
	protected int leftMargin = 2;
	
	/**
	 * Right margin of the menu.
	 */
	protected int rightMargin = 2;
	
	
	//preferred size
	
	/**
	 * Preferred height for the toolbar button.
	 */
	protected int preferredHeight = 30;
	
	/**
	 * Preferred width for the toolbar button.
	 */
	protected int preferredWidth = 40;
	
	protected boolean mouseOver = false;
	
	protected boolean mousePressed = false;
	
	protected MouseAdapter mouseListener = null;
	
	private boolean isFocused = false;
	private FocusListener focusListener = null;
	
	
	/**
	 * @param hoverIcon
	 */
	public ToolBarButtonUI(Icon hoverIcon) {
		super();
		this.hoverIcon = hoverIcon;
	}
	
	@Override
	protected void installDefaults(AbstractButton b) {
		super.installDefaults(b);

		b.setBorder(new EmptyBorder(0, 0, 0, 0));
	}
	
	@Override
	public void paint(Graphics g, JComponent c) {
		JButton buttonComponent = (JButton) c;
		boolean enabled = buttonComponent.isEnabled();
		if (mouseListener == null) {
			createMouseLister();
			buttonComponent.addMouseListener(mouseListener);
		}
		
		if (focusListener == null) {
			createFocusListener();
			buttonComponent.addFocusListener(focusListener);
		}
		
		mouseOver = isMouseOver(c) || isFocused;
		
		Graphics2D g2d = (Graphics2D) g;
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
				RenderingHints.VALUE_ANTIALIAS_ON);
		
		int w = c.getWidth() - (this.leftMargin + this.rightMargin);
		int h = c.getHeight() - (this.topMargin + this.bottomMargin);

		int x = this.leftMargin;
		int y = this.topMargin;
		
		double arc = (double) h * .4;
			
		if (mouseOver && enabled) {
			paintButtonBorder(g2d, 
					buttonComponent, 
					x, 
					y, 
					w, 
					h, 
					arc, 
					mousePressed);
		}
		paintButtonBackGround(g2d, 
				buttonComponent, 
				x, 
				y, 
				w, 
				h, 
				arc, 
				mousePressed, 
				mouseOver, 
				enabled);
	}

	/**
	 * Paints the background of the button and the icon depending on
	 * mouse behavior.
	 * 
	 * @param g Graphic object to paint on.
	 * @param b The button of the toolbar.
	 * @param x The position of the button on the x axis.
	 * @param y The position of the button on the y axis.
	 * @param w The width of the button.
	 * @param h The height of the button.
	 * @param arc The roundness of the button.
	 * @param isPressed True if the button is pressed, otherwise false.
	 * @param isRollOver True if the mouse is over, otherwise false.
	 */
	private void paintButtonBackGround(Graphics2D g, AbstractButton b, 
			int x, int y, int w, int h, double arc, 
			boolean isPressed, boolean isRollOver, boolean enabled) {
		
		Shape buttonShape = new RoundRectangle2D.Double((double) x, 
				(double) y, 
				(double) w, 
				(double) h, 
				arc, 
				arc);
		Shape clip = g.getClip();
		
		g.setClip(buttonShape);
		g.setColor(Color.WHITE);

		if (enabled) {
			if (isRollOver) {
				if (isPressed) {
					Paint paint = new GradientPaint(0, 
							y, 
							this.pressedGradientTop, 
							0, 
							y + h, 
							this.pressedGradientBottom);
					g.setPaint(paint);
					g.fillRoundRect(x+1, 
							y+1, 
							w+1, 
							h  - 2, 
							(int) arc, 
							(int) arc);
					
				} else {
					Paint paint = new GradientPaint(0, 
							y, 
							this.hoverGradientTop, 
							0, 
							y + h, 
							this.hoverGradientBottom);
					g.setPaint(paint);
					g.fillRoundRect(x+1, 
							y+1, 
							w+1, 
							h  - 2, 
							(int) arc, 
							(int) arc);
				}
			}
	
			if (isRollOver && !isPressed) {
				Icon hoverIcon = this.hoverIcon;
				if (hoverIcon != null) {
					x = (w - hoverIcon.getIconWidth()) / 2 + this.leftMargin;
					y = (h - hoverIcon.getIconHeight()) / 2 + 2;
						hoverIcon.paintIcon(b, g, x, y);
					}
			} else if (!isRollOver && !isPressed) {
				Icon normalIcon = b.getIcon();
				if (normalIcon != null) {
					x = (w - normalIcon.getIconWidth()) / 2 + this.leftMargin;
					y = (h - normalIcon.getIconHeight()) / 2 + 2;
		
					normalIcon.paintIcon(b, g, x, y);
				}
				
			} else {
				Icon normalIcon = b.getIcon();
				if (normalIcon != null) {
					x = (w - normalIcon.getIconWidth()) / 2 + this.leftMargin;
					y = (h - normalIcon.getIconHeight()) / 2 + 2;
					
					normalIcon.paintIcon(b, g, x, y);
				}
			}
		} else {
			Icon normalIcon = b.getIcon();
			if (normalIcon != null) {
				x = (w - normalIcon.getIconWidth()) / 2 + this.leftMargin;
				y = (h - normalIcon.getIconHeight()) / 2 + 2;
				
				AlphaComposite alpha = AlphaComposite.SrcOver.derive(0.30f);
				g.setComposite(alpha);
				
				normalIcon.paintIcon(b, g, x, y);
			}
		}
		g.setClip(clip);
		
	}

	/**
	 * Paints the border of the button  depending on
	 * mouse behavior.
	 * 
	 * @param g Graphic object to paint on.
	 * @param b The button of the toolbar.
	 * @param x The position of the button on the x axis.
	 * @param y The position of the button on the y axis.
	 * @param w The width of the button.
	 * @param h The height of the button.
	 * @param arc The roundness of the button.
	 * @param isPressed True if the button is pressed, otherwise false.
	 */
	private void paintButtonBorder(Graphics2D g, AbstractButton c, 
			int x, int y, int w, int h, double arc, boolean isPressed) {
		Shape buttonShape = new RoundRectangle2D.Double(
				(double) x, 
				(double) y, 
				(double) w, 
				(double) h, 
				arc, 
				arc);
		Shape clip = g.getClip();

		g.setClip(x,y+h-(h/2),w,y+h);
		g.translate(0, 0);
		g.draw(buttonShape);

		g.setClip(clip);
		g.setColor(borderColor);
		g.setStroke(borderStroke);
		g.translate(0, 0);
		g.draw(buttonShape);

	}
	
	/**
	 * Calculates the preferred height for the toolbar button.
	 * 
	 * @return The calculated preferred height for the toolbar button.
	 */
	public int calculateHeight() {	
		return this.preferredHeight;
	}
	
	/**
	 * Calculates the preferred width for the toolbar button.
	 * 
	 * @return The calculated preferred width for the toolbar button.
	 */
	public int calculateWidth() {
		return this.preferredWidth;
	}
	
	@Override
	public Dimension getPreferredSize(JComponent c) {
		if(c instanceof JButton)
			return new Dimension(this.calculateWidth(), this.calculateHeight());
		else
			return super.getPreferredSize(c);
	}
	
	@Override
	public Dimension getMinimumSize(JComponent c) {
		if(c instanceof JButton)
			return getPreferredSize(c);
		else
			return super.getMinimumSize(c);
	}
	
	@Override
	public Dimension getMaximumSize(JComponent c) {
		if(c instanceof JButton)
			return getPreferredSize(c);
		else
			return super.getMaximumSize(c);
	}
	
	private void createMouseLister() {
		mouseListener = new MouseAdapter() {
			public void mouseEntered(MouseEvent mouseEvent) {
				mouseOver = true;
				mouseEvent.getComponent().repaint();
			}
			
			public void mouseExited(MouseEvent mouseEvent) {
				mouseOver = false;
				mouseEvent.getComponent().repaint();
			}
		      
			public void mousePressed(MouseEvent mouseEvent) {
				mousePressed = true;
				mouseEvent.getComponent().repaint();
			}
		      
			public void mouseReleased(MouseEvent mouseEvent) {
				mousePressed = false;
				mouseEvent.getComponent().repaint();
			}
		};
	}
	
	private boolean isMouseOver(JComponent c) {
		if (!c.isShowing())
			return false;
		
		PointerInfo info = MouseInfo.getPointerInfo();
        Point locMouse = info.getLocation(); 
        Point locComp = c.getLocationOnScreen();
        
        Rectangle rect = new Rectangle((int)locComp.getX(), 
        		(int)locComp.getY(), 
        		c.getWidth(), 
        		c.getHeight());
        
        return rect.contains(locMouse);
	}
	
	private void createFocusListener() {
		focusListener = new FocusListener() {
			
			@Override
			public void focusGained(FocusEvent e) {
				isFocused = true;
				
			}
			@Override
			public void focusLost(FocusEvent e) {
				mouseOver = false;
				isFocused = false;
			}
		};
	}
}